#ifndef TENSOR_H
#define TENSOR_H

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
#include <iterator>
#include <numeric>

class tensor {
public:
  tensor();
  // no need for explicit here
  tensor(double v);
  tensor(int dim, size_t shape[], double data[]);

  int get_dim() const;
  size_t getTotalSize() const;
  std::vector<double>& get_data_vec();

  // for ctypes only, use with care
  size_t *get_shape_array();
  double *get_data_array();

  // debug output
  friend std::ostream& operator<<(std::ostream& os, const tensor& t);

  // friend is better than member, because it allows
  // double + tensor (implicit conversion)
  friend tensor operator+(const tensor &lhs, const tensor &rhs);
  friend tensor operator-(const tensor &lhs, const tensor &rhs);
  friend tensor operator*(const tensor &lhs, const tensor &rhs);
   
  friend tensor ReLU(const tensor &x);
  friend tensor Flatten(const tensor &x);
  friend tensor Inut2d(const tensor &x);
  friend tensor Linear(const tensor &w, const tensor &x, const tensor &b);
  friend tensor MaxPool2d(const tensor &x, const int kernel_size, const int stide);


  template <class T>
  friend tensor helper(const tensor &lhs, const tensor &rhs);

private:

  std::vector<size_t> shape_;
  std::vector<double> data_;
}; // class tensor

#endif // TENSOR_H
